#ifndef _SE_BASE_
#define _SE_BASE_

// For some attributes.
#include <cfloat>
#include <climits>

// For some meta types.
#include "Meta/Singleton.hpp"
#include "Meta/SourceLibrary.hpp"

// For some basic types.
#include <array>
#include <memory>
#include <vector>
#include <string>
#include <cstdint>
#include <sstream>
#include <functional>
#include <string_view>
#include <unordered_map>

// For profile.
#include "Debug/Profile.h"

namespace SE
{
	// --------------------------
	/* Global type definations */
	// Numeric types.
	using I8			= std::int8_t;
	using I16			= std::int16_t;
	using I32			= std::int32_t;
	using I64			= std::int64_t;
	using U8			= std::uint8_t;
	using U16			= std::uint16_t;
	using U32			= std::uint32_t;
	using U64			= std::uint64_t;
	using Float			= float;
	using Double		= double;

	// Boolean type.
	using Boolean		= bool;

	// String types.
	using String		= std::string;
	using StringView	= std::string_view;
	using StringStream	= std::stringstream;

	// Memory types.
	template<typename _Ty>
	using Ref			= std::shared_ptr<_Ty>;
	template<typename _Ty>
	using Scope			= std::unique_ptr<_Ty>;
	template<typename _Ty>
	using Weak			= std::weak_ptr<_Ty>;

	// Container types.
	template<typename _Ty>
	using Vector		= std::vector<_Ty>;
	template<typename _Ty, U64 _Size>
	using Array			= std::array<_Ty, _Size>;
	template<typename _Ty1, typename _Ty2>
	using HashMap		= std::unordered_map<_Ty1, _Ty2>;
	template <class _Ty1, class _Ty2>
	using Pair			= std::pair<_Ty1, _Ty2>;

	// Composite types.
	class Event;
	using EventHandler	= std::function<Boolean(Event&)>;
	using EventCallback = std::function<void(Event&)>;

	/// <summary>
	/// Create the 'Ref' type object.
	/// </summary>
	/// <typeparam name="_Ty"> The object type you want to create. </typeparam>
	/// <typeparam name="..._Tys"> The initialization parameter types for creating target object. </typeparam>
	/// <param name="...args"> The initialization parameters list for creating target object. </param>
	/// <returns> The 'Ref' type object. </returns>
	template<typename _Ty, typename ..._Tys>
	inline Ref<_Ty> CreateRef(_Tys&& ...args)
	{
		return std::make_shared<_Ty>(std::forward<_Tys>(args)...);
	}
	/// <summary>
	/// Create the 'Scope' type object.
	/// </summary>
	/// <typeparam name="_Ty"> The object type you want to create. </typeparam>
	/// <typeparam name="..._Tys"> The initialization parameter types for creating target object. </typeparam>
	/// <param name="...args"> The initialization parameters list for creating target object. </param>
	/// <returns> The 'Scope' type object. </returns>
	template<typename _Ty, typename ..._Tys>
	inline Scope<_Ty> CreateScope(_Tys&& ...args)
	{
		return std::make_unique<_Ty>(std::forward<_Tys>(args)...);
	}
	// --------------------------


	// --------------------------
	/* Macro utilities */
	// Bit offset.
	#define BIT(x) (1 << (x))

	#define BindEnumOperationAddToBoolean(enumType, enumBitType) \
		inline Boolean operator&(const enumType val1, const enumType val2) \
		{ \
			return static_cast<enumBitType>(val1) & static_cast<enumBitType>(val2); \
		}
	
	// Assertion.
	#ifdef SE_DEBUG
		#define SE_ENABLE_ASSERT
	#endif // !SE_DEBUG

	#ifdef SE_ENABLE_ASSERT
		#define SE_ASSERT(pred, ...) if (!(pred)) { LOG_CRITI(__VA_ARGS__); __debugbreak(); }
	#else
		#define SE_ASSERT(pred, ...)
	#endif // !SE_ENABLE_ASSERT
	// --------------------------
} // !namespace SE

#endif // !_SE_BASE_